﻿using System.Diagnostics;
using System.Windows;
using HalconDotNet;
using MachineVision.Core.Extensions;
using MachineVision.Core.TemplateMatch.Shared;
using Prism.Mvvm;
#nullable disable
namespace MachineVision.Core.TemplateMatch.ShapeModel
{
    public class ShapeModelService : BindableBase, ITemplateMatchService
    {
        public ShapeModelService()
        {
            Roi = new ROIParameter();
            Info = new()
            {
                Name = "find_shpe_model",
                Description = "Find the best matches of a shape model in an image",
                Parameters = new List<MethodParameter>()
                {
                    new MethodParameter()
                    {
                        Name = "Image",
                        Description = "Input image in which the model should be found."
                    },
                    new MethodParameter()
                    {
                        Name = "ModelID",
                        Description = "Handle of the model."
                    },
                    new MethodParameter()
                    {
                        Name = "AngleStart",
                        Description = "Smallest rotation of the model."
                    },
                    new MethodParameter()
                    {
                        Name = "AngleExtent",
                        Description = "Extent of the rotation angles."
                    },
                    new MethodParameter()
                    {
                        Name = "MinScore",
                        Description = "Minimum score of the instances of the model to be found."
                    },
                    new MethodParameter()
                    {
                        Name = "NumMatches",
                        Description =
                            "Number of instances of the model to be found (or 0 for all matches)."
                    },
                    new MethodParameter()
                    {
                        Name = "MaxOverlap",
                        Description = "Maximum overlap of the instances of the model to be found."
                    },
                    new MethodParameter()
                    {
                        Name = "SubPixel",
                        Description = "Subpixel accuracy if not equal to 'none'."
                    },
                    new MethodParameter()
                    {
                        Name = "NumLevels",
                        Description =
                            "Number of pyramid levels used in the matching (and lowest pyramid level to use if |NumLevels| = 2)."
                    },
                    new MethodParameter()
                    {
                        Name = "Greediness",
                        Description =
                            "“Greediness” of the search heuristic (0: safe but slow; 1: fast but matches may be missed)."
                    },
                    new MethodParameter()
                    {
                        Name = "Row",
                        Description = "Row coordinate of the found instances of the model."
                    },
                    new MethodParameter()
                    {
                        Name = "Column",
                        Description = "Column coordinate of the found instances of the model."
                    },
                    new MethodParameter()
                    {
                        Name = "Angle",
                        Description = "Rotation angle of the found instances of the model."
                    },
                    new MethodParameter()
                    {
                        Name = "Score",
                        Description = "Score of the found instances of the model."
                    }
                },
                Preducessors = new List<string>()
                {
                    "create_shape_model",
                    "read_shape_model",
                    "write_shape_model"
                }
            };
            TemplateParameter = new();
            RunParameter = new();
            Setting = new MatchResultSetting();
            MatchResult = new MatchResult();
        }

        /// <summary>
        /// 模板ID
        /// </summary>
        private HTuple modelId;
        private HTuple Row;
        private HTuple Column;
        private HTuple Angle;
        private HTuple Score;
        private ShapeModelInputParameter templateParameter;
        private HWindow window;

        public HWindow Window
        {
            get { return window; }
            set
            {
                window = value;
                RaisePropertyChanged();
            }
        }

        /// <summary>
        /// 形状匹配模板参数
        /// </summary>
        public ShapeModelInputParameter TemplateParameter
        {
            get { return templateParameter; }
            set
            {
                templateParameter = value;
                RaisePropertyChanged();
            }
        }
        private ShapeModelRunParameter runParameter;

        /// <summary>
        /// 形状匹配运行参数
        /// </summary>
        public ShapeModelRunParameter RunParameter
        {
            get { return runParameter; }
            set
            {
                runParameter = value;
                RaisePropertyChanged();
            }
        }
        private MatchResult matchResult;

        public MatchResult MatchResult
        {
            get { return matchResult; }
            set
            {
                matchResult = value;
                RaisePropertyChanged();
            }
        }
        public MethodInfo Info { get; set; }
        public ROIParameter Roi { get; set; }
        public MatchResultSetting Setting { get; set; }

        public Task CreateTemplateAsync(HObject image, HObject hobject)
        {
            if (image == null)
                throw new Exception("不正确的图像格式");
            if (hobject == null)
                throw new Exception("未创建匹配模板");
            HObject imageReduced;
            HOperatorSet.GenEmptyObj(out imageReduced);
            //減少域
            HOperatorSet.ReduceDomain(image, hobject, out imageReduced);
            HOperatorSet.CreateShapeModel(
                imageReduced,
                TemplateParameter.NumLevels,
                TemplateParameter.AngleStart,
                TemplateParameter.AngleExtent,
                TemplateParameter.AngleStep,
                TemplateParameter.Optimization,
                TemplateParameter.Metric,
                TemplateParameter.Contrast,
                TemplateParameter.MinContrast,
                out modelId
            );
            return Task.CompletedTask;
        }

        public double GetRunTimeSpan(Action action)
        {
            Stopwatch stopwatch = Stopwatch.StartNew();
            action();
            stopwatch.Stop();
            return stopwatch.ElapsedMilliseconds;
        }

        public void Run(HObject image)
        {
            MatchResult result = new MatchResult();
            if (image == null)
                throw new Exception("图像格式不正确");
            if (modelId == default(HTuple))
                throw new Exception("未创建匹配模板");
            var timespan = GetRunTimeSpan(() =>
            {
                HObject reduceimg;
                HOperatorSet.GenEmptyObj(out reduceimg);
                //该扩展方法用于判断是否设定检测区域，如果参数有效则返回需要裁剪的区域，否则返回本身
                reduceimg = image.ReduceDomain(Roi);
                //保存图像到本机
                HOperatorSet.WriteImage(
                    reduceimg,
                    "bmp",
                    0,
                    @$".\Assets\RunImages\TemplateMatch_{DateTime.Now.ToString("yyyyMMddHHmmssfff")}.bmp"
                );
                //开始匹配
                HOperatorSet.FindShapeModel(
                    reduceimg,
                    modelId,
                    new HTuple(TemplateParameter.AngleStart).TupleRad(),
                    new HTuple(TemplateParameter.AngleExtent).TupleRad(),
                    RunParameter.MinScore,
                    RunParameter.NumMatches,
                    RunParameter.MaxOverlop,
                    RunParameter.SubPixel,
                    RunParameter.NumLevels,
                    RunParameter.Greediness,
                    out Row,
                    out Column,
                    out Angle,
                    out Score
                );
            });
            //计算形状模板轮廓
            HOperatorSet.GetShapeModelContours(out HObject modelContours, modelId, 1);

            for (int i = 0; i < Score.Length; i++)
            {
                var row1 = Math.Round(Row.DArr[i], 3);
                var col1 = Math.Round(Column.DArr[i], 3);
                var ang1 = Math.Round(Angle.DArr[i], 3);
                var score1 = Math.Round(Score.DArr[i], 3);

                //计算轮廓匹配的目标位置对象
                HOperatorSet.VectorAngleToRigid(0, 0, 0, row1, col1, ang1, out HTuple homMat2D);
                HOperatorSet.AffineTransContourXld(
                    modelContours,
                    out HObject contoursAddineTrans,
                    homMat2D
                );

                result.Results.Add(
                    new TemplateMatchResult()
                    {
                        Index = i + 1,
                        Row = row1,
                        Column = col1,
                        Angle = Math.Round((180 / Math.PI) * ang1, 3), //根据弧度转换成角度
                        Score = score1,
                        Contours = contoursAddineTrans,
                    }
                );
                if (Setting.IsShowCenter)
                    HOperatorSet.DispCross(Window, row1, col1, 20, score1);
                if (Setting.IsShowMatchRange)
                    Window.DispObj(contoursAddineTrans);
                if (Setting.IsShowDisplayText)
                    Window.SetString(
                        $"{row1} : {col1} : {score1}",
                        "image",
                        row1,
                        col1,
                        "black",
                        "true"
                    );
            }
            result.TimeSpan = timespan;
            result.Message =
                $"{DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss")}：匹配耗时:{timespan}ms,"
                + $"{(result.Results.Count > 0 ? $"匹配个数:{result.Results.Count}" : "未匹配相关数据")}";
            MatchResult = result;

            //return result;
        }
    }
}
